package io.kiki.sba.registry.server.shared.providedata;


import io.kiki.sba.registry.util.ConcurrentUtils;
import io.kiki.sba.registry.util.StringFormatter;
import io.kiki.sba.registry.util.WakeUpLoopExecuteTask;
import lombok.NonNull;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;


public abstract class AbstractFetchPersistenceSystemProperty<T extends SystemDataStorage, E extends SystemDataStorage> implements FetchSystemPropertyService {

    protected static final long INIT_VERSION = -1L;
    private static final Logger logger = LoggerFactory.getLogger(AbstractFetchPersistenceSystemProperty.class);
    protected final AtomicReference<T> storage = new AtomicReference<>();
    protected final WatchDog watchDog = new WatchDog();
    private final String dataInfoId;
    private final AtomicBoolean watcherInited = new AtomicBoolean(false);

    public AbstractFetchPersistenceSystemProperty(String dataInfoId, @NonNull T t) {

        this.dataInfoId = dataInfoId;
        storage.set(t);
    }

    protected abstract int getSystemPropertyIntervalMillis();

    protected boolean doFetchData() {
        T expect = storage.get();
        E fetchData = fetchFromPersistence();

        if (fetchData == null) {
            logger.error("Fetch persistence system data={}, currentVersion={}, updateVersion={}", dataInfoId, expect, fetchData.getVersion());
            return false;
        }

        if (fetchData.getVersion() < expect.getVersion()) {
            logger.warn("Fetch persistence system data={}, currentVersion={}, updateVersion={}", dataInfoId, expect, fetchData.getVersion());
            return false;
        } else
            if (fetchData.getVersion() == expect.getVersion()) {
                return true;
            }

        return doProcess(expect, fetchData);
    }

    @Override
    public boolean support(String dataInfoId) {
        return StringUtils.equals(this.dataInfoId, dataInfoId);
    }

    @Override
    public boolean doFetch() {
        watchDog.wakeup();
        return true;
    }

    @Override
    public boolean start() {
        doFetchData();
        if (watcherInited.compareAndSet(false, true)) {
            ConcurrentUtils.createDaemonThread(StringFormatter.format("FetchPersistenceSystemProperty-{}", dataInfoId), watchDog).start();
        }

        return watcherInited.get();
    }

    protected boolean compareAndSet(T expect, T update) {
        return storage.compareAndSet(expect, update);
    }

    protected abstract E fetchFromPersistence();

    protected abstract boolean doProcess(T expect, E data);

    protected final class WatchDog extends WakeUpLoopExecuteTask {

        @Override
        public void _execute_() {
            doFetchData();
        }

        @Override
        public int getWaitingMillis() {
            return getSystemPropertyIntervalMillis();
        }
    }
}
